﻿using InkjetControl.NLog;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.IO.Ports;
using System.Text;
using System.Threading;

namespace InkjetControl.plc
{
    public class SerialPortService
    {
        private readonly SerialPort serialPort;
        private Thread receiverThread;
        private bool receiverFlag;
        private readonly ByteBuffer receiverBuffer;

        public const int MIN_BUFFER_LEN = 255;
        private const int MAX_BUFFER_LEN = 60000;
        public readonly object LockReceiveBuffer = new { };

        public ConcurrentQueue<byte[]> SenderBuffer { get; }
/*
        public delegate void DateReceiverHandler(byte[] buffer);
        public event DateReceiverHandler DateReceiver;
*/
        public SerialPortService()
        {
            serialPort = new SerialPort();
            receiverBuffer = new ByteBuffer(MAX_BUFFER_LEN);
            SenderBuffer = new ConcurrentQueue<byte[]>();
        }

        public void OpenPort(string portName, int baudRate)
        {
            if (serialPort.IsOpen)
            {
                return;
            }
            try
            {
                serialPort.PortName = portName;
                serialPort.BaudRate = baudRate;
                serialPort.DataBits = 8;
                serialPort.StopBits = StopBits.One;
                serialPort.Parity = Parity.Even;

                serialPort.Open();

                serialPort.DataReceived += SerialPort_DataReceived;
                
            }
            catch (IOException ex)
            {
                Log.Logger.Error("OpenPort Error: {0}", ex.Message);
            }

            try
            {
                if (receiverThread == null)
                {
                    receiverThread = new Thread(() => ReceiverThreadStart())
                    {
                        IsBackground = true
                    };
                }
                if (!receiverFlag)
                {
                    receiverFlag = true;
                    receiverThread.Start();
                }
            }
            catch (ThreadStartException)
            {
                
            }

        }

        private void ReceiverThreadStart()
        {
            byte[] dataBuf = new byte[1024];
            while (true)
            {
                if (!receiverFlag)
                {
                    break;
                }

                if (receiverBuffer.DataCount < MIN_BUFFER_LEN)
                {
                    try
                    {
                        Thread.Sleep(200);
                    }
                    catch (Exception ex)
                    {
                        Log.Logger.Warn( ex, "sleep" );
                    }
                    continue;
                }
                lock (LockReceiveBuffer)
                {
                    receiverBuffer.ReadBuffer(dataBuf, 0, MIN_BUFFER_LEN);
                    receiverBuffer.Clear(MIN_BUFFER_LEN);

                }
            }
        }

        private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            Thread.Sleep(200);
            int readConut = serialPort.BytesToRead;
            if (readConut > 0)
            {
                byte[] buffer = new byte[readConut];
                int readlen = serialPort.Read(buffer, 0, readConut);

                ProcessBytesTask(buffer, readlen);
            }
        }

        public void ProcessBytesTask(byte[] data, int len)
        {
            /*string datastr = HexUtils.BytesToHexString(data);
            Log.Info("ProcessBytesTask: temp = {0:X2}", datastr);*/
            
            if (len > 0)
            {
                lock (LockReceiveBuffer)
                {
                    while (len + receiverBuffer.DataCount > 1024)       // 缓存溢出处理
                    {
                        _ = Monitor.Wait(LockReceiveBuffer, 10000);
                    }
                    receiverBuffer.WriteBuffer(data, 0, len);
                    Monitor.PulseAll(LockReceiveBuffer);
                }
                //string hexString = HexUtils.BytesToHexString(data);
                //Log.Info("DataReceived: {0}", hexString);
            }

        }

        public void ClosePort()
        {
            serialPort.Close();
        }
    }
}